package com.yzh.luckydraw.domain.activity.service.partake;

import com.yzh.luckydraw.common.Constants;
import com.yzh.luckydraw.common.Result;
import com.yzh.luckydraw.domain.activity.model.req.PartakeReq;
import com.yzh.luckydraw.domain.activity.model.res.PartakeResult;
import com.yzh.luckydraw.domain.activity.model.res.StockResult;
import com.yzh.luckydraw.domain.activity.model.vo.ActivityBillVO;
import com.yzh.luckydraw.domain.activity.model.vo.UserTakeActivityVO;
import com.yzh.luckydraw.domain.supports.ids.IIdGenerator;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;

/**
 * 活动领取模板抽象类
 */
public abstract class BaseActivityPartake extends ActivityPartakeSupport implements IActivityPartake {
    @Resource
    private Map<Constants.Ids, IIdGenerator> idGeneratorMap;

    @Override
    public PartakeResult doPartake(PartakeReq req) {
        //1.查询是否存在未执行的抽奖领取活动单(user_take_activity中state=0, 领取了但抽奖过程失败的, 可以直接返回领取结果继续抽奖)
        UserTakeActivityVO userTakeActivityVO = this.queryNoConsumedTakeActivityOrder(req.getActivityId(), req.getuId());
        if (userTakeActivityVO != null) {
            return buildPartakeResult(userTakeActivityVO.getStrategyId(), userTakeActivityVO.getTakeId(), Constants.ResponseCode.NOT_CONSUMED_TAKE);
        }

        //2.查询活动账单
        ActivityBillVO activityBillVO = super.queryActivityBill(req);

        //3.活动信息校验处理【活动库存、状态、日期、个人参与次数】
        Result checkResult = this.checkActivityBill(req, activityBillVO);
        if (!checkResult.getCode().equals(Constants.ResponseCode.SUCCESS.getCode())) {
            return new PartakeResult(checkResult.getCode(), checkResult.getInfo());
        }

        //4.扣减活动库存(目前直接扣减activity中的库存，后续优化为Redis)
//        Result subResult = this.subtractionActivityStock(req);
        StockResult stockResult = this.subtractionActivityStockByRedis(req.getuId(), req.getActivityId(), activityBillVO.getStockCount(), activityBillVO.getEndTime());
        if (!stockResult.getCode().equals(Constants.ResponseCode.SUCCESS.getCode())) {
            this.recoverActivityCacheStockByRedis(req.getActivityId(), stockResult.getStockKey(), stockResult.getCode());
            return new PartakeResult(stockResult.getCode(), stockResult.getInfo());
        }

        //5.领取活动信息(把活动信息写入用户表)
        Long takeId = idGeneratorMap.get(Constants.Ids.SnowFlake).nextId();
        Result grabResult = this.grabActivity(req, activityBillVO, takeId);
        if (!grabResult.getCode().equals(Constants.ResponseCode.SUCCESS.getCode())) {
            this.recoverActivityCacheStockByRedis(req.getActivityId(), stockResult.getStockKey(), grabResult.getCode());
            return new PartakeResult(grabResult.getCode(), grabResult.getInfo());
        }

        //6.删除key(到这里说明用户领取活动信息已经成功，每个库存key都是唯一的，已经不再需要了，所以这里需要删除key)
        this.recoverActivityCacheStockByRedis(req.getActivityId(), stockResult.getStockKey(), Constants.ResponseCode.SUCCESS.getCode());

        //7.封装结果(返回策略ID，用于继续完成抽奖步骤)
        return buildPartakeResult(activityBillVO.getStrategyId(), takeId, activityBillVO.getStockCount(), stockResult.getStockSurplusCount(), Constants.ResponseCode.SUCCESS);
    }

    protected abstract UserTakeActivityVO queryNoConsumedTakeActivityOrder(Long activity, String uId);

    /**
     * 活动信息校验处理，检验活动库存、状态、日期、个人参与次数
     *
     * @param partakeReq 参与活动请求
     * @param billVO     活动账单
     * @return
     */
    protected abstract Result checkActivityBill(PartakeReq partakeReq, ActivityBillVO billVO);

    /**
     * 扣减活动库存
     *
     * @param partakeReq 参与活动请求
     * @return
     */
    protected abstract Result subtractionActivityStock(PartakeReq partakeReq);

    /**
     * 扣减活动库存（Redis）
     *
     * @param uId        用户ID
     * @param activityId 活动ID
     * @param stockCount 活动库存activity表的stockCount
     * @return
     */
    protected abstract StockResult subtractionActivityStockByRedis(String uId, Long activityId, Integer stockCount, Date endTime);

    /**
     * 恢复活动库存（Redis）如果出现异常，则需要从缓存进行库存恢复，只能保证不超卖，可以由任务进行补偿
     *
     * @param activityId
     * @param tokenKey
     * @param code
     */
    protected abstract void recoverActivityCacheStockByRedis(Long activityId, String tokenKey, String code);

    /**
     * 领取活动
     *
     * @param req    参与活动请求
     * @param billVO 活动账单
     * @return
     */
    protected abstract Result grabActivity(PartakeReq req, ActivityBillVO billVO, Long takeId);
    
    /**
     * 封装结果【返回的策略ID，用于继续完成抽奖步骤】
     *
     * @param strategyId 策略ID
     * @param takeId     领取ID
     * @param code       状态码
     * @return 封装结果
     */
    private PartakeResult buildPartakeResult(Long strategyId, Long takeId, Constants.ResponseCode code) {
        PartakeResult partakeResult = new PartakeResult(code.getCode(), code.getInfo());
        partakeResult.setStrategyId(strategyId);
        partakeResult.setTakeId(takeId);
        return partakeResult;
    }

    /**
     * 封装结果【返回的策略ID，用于继续完成抽奖步骤】
     * @param strategyId        策略ID
     * @param takeId            领取ID
     * @param stockCount        库存
     * @param stockSurplusCount 剩余库存
     * @param code              状态码
     * @return
     */
    private PartakeResult buildPartakeResult(Long strategyId, Long takeId, Integer stockCount, Integer stockSurplusCount, Constants.ResponseCode code) {
        PartakeResult partakeResult = new PartakeResult(code.getCode(), code.getInfo());
        partakeResult.setStrategyId(strategyId);
        partakeResult.setTakeId(takeId);
        partakeResult.setStockCount(stockCount);
        partakeResult.setStockSurplusCount(stockSurplusCount);
        return partakeResult;
    }
}
